Developers commonly spend significant amounts of time debugging programs. Developers often apply the scientific method when debugging programs; for instance, a developer may observe behavior of a program on different inputs, first to reproduce a bug, then to localize the bug in source code of the program. Reproducing, localizing, and then fixing bugs typically involve forming and validating hypotheses about root causes of the bugs. A root cause of a bug is a first point at which a state of a program diverges from an intended state. To validate a hypothesis, a developer often halts program execution and examines the state of the program. Especially early in debugging, when the developer's query is still elusive, execution is commonly halted too soon or too late to validate a hypothesis. Overshooting (e.g., late halting of the execution of the program) can be expensive because returning to an earlier point in an execution of a program commonly involves restarting and re-running the program.
A virtual machine can be useful for capturing a state of a program. For example, a virtualization layer can act as a boundary for encapsulating program state, which can enable snapshotting, migrating, and resuming complex, multi-level software applications. A virtual machine interface (VMI) of a virtual machine can provide an introspection point for logging events that affect program execution.
Some conventional virtual machines, which virtualize at the x86-level, provide strong and efficient isolation. By virtualizing at the x86-level, these conventional virtual machines can provide precise snapshots that capture hardware-level information. However, with these conventional approaches, a full virtual machine snapshot can be on the order of hundreds of MBs in size, and an x86-level event log can grow on the order of hundreds of MBs per day.
A debugger can be used to halt program execution and examine the state of the program (e.g., via a graphical user interface (GUI)). However, tool support for debugging has remained relatively unchanged for many years. Accordingly, the cost of restarting and re-running the program when a breakpoint is placed too late in the execution of the program remains.